1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   */
19  package org.codehaus.groovy.classgen.asm;
20  
21  import groovy.lang.GroovyRuntimeException;
22  
23  import java.lang.reflect.Constructor;
24  import java.util.Map;
25  
26  import org.codehaus.groovy.GroovyBugError;
27  import org.codehaus.groovy.ast.ClassHelper;
28  import org.codehaus.groovy.ast.ClassNode;
29  import org.codehaus.groovy.ast.ConstructorNode;
30  import org.codehaus.groovy.ast.InnerClassNode;
31  import org.codehaus.groovy.ast.InterfaceHelperClassNode;
32  import org.codehaus.groovy.ast.MethodNode;
33  import org.codehaus.groovy.classgen.AsmClassGenerator;
34  import org.codehaus.groovy.classgen.GeneratorContext;
35  import org.codehaus.groovy.control.CompilerConfiguration;
36  import org.codehaus.groovy.control.SourceUnit;
37  import org.objectweb.asm.ClassVisitor;
38  import org.objectweb.asm.MethodVisitor;
39  import org.objectweb.asm.Opcodes;
40  
41  public class WriterController {
42  
43      private static Constructor indyWriter, indyCallSiteWriter, indyBinHelper;
44      static {
45          try {
46              ClassLoader cl = WriterController.class.getClassLoader();
47              Class indyClass = cl.loadClass("org.codehaus.groovy.classgen.asm.indy.InvokeDynamicWriter");
48              indyWriter = indyClass.getConstructor(WriterController.class);
49              indyClass = cl.loadClass("org.codehaus.groovy.classgen.asm.indy.IndyCallSiteWriter");
50              indyCallSiteWriter = indyClass.getConstructor(WriterController.class);
51              indyClass = cl.loadClass("org.codehaus.groovy.classgen.asm.indy.IndyBinHelper");
52              indyBinHelper = indyClass.getConstructor(WriterController.class);
53          } catch (Exception e) {
54              indyWriter = null;
55              indyCallSiteWriter = null;
56              indyBinHelper = null;
57          }
58      }
59      private AsmClassGenerator acg;
60      private MethodVisitor methodVisitor;
61      private CompileStack compileStack;
62      private OperandStack operandStack;
63      private ClassNode classNode;
64      private CallSiteWriter callSiteWriter;
65      private ClassVisitor cv;
66      private ClosureWriter closureWriter;
67      private String internalClassName;
68      private InvocationWriter invocationWriter;
69      private BinaryExpressionHelper binaryExpHelper, fastPathBinaryExpHelper;
70      private UnaryExpressionHelper unaryExpressionHelper, fastPathUnaryExpressionHelper;
71      private AssertionWriter assertionWriter;
72      private String internalBaseClassName;
73      private ClassNode outermostClass;
74      private MethodNode methodNode;
75      private SourceUnit sourceUnit;
76      private ConstructorNode constructorNode;
77      private GeneratorContext context;
78      private InterfaceHelperClassNode interfaceClassLoadingClass;
79      public boolean optimizeForInt = true;
80      private StatementWriter statementWriter;
81      private boolean fastPath = false;
82      private TypeChooser typeChooser;
83      private int bytecodeVersion = Opcodes.V1_5;
84      private int lineNumber = -1;
85      private int helperMethodIndex = 0;
86  
87      public void init(AsmClassGenerator asmClassGenerator, GeneratorContext gcon, ClassVisitor cv, ClassNode cn) {
88          CompilerConfiguration config = cn.getCompileUnit().getConfig();
89          Map<String,Boolean> optOptions = config.getOptimizationOptions();
90          boolean invokedynamic=false;
91          if (optOptions.isEmpty()) {
92              // IGNORE
93          } else if (Boolean.FALSE.equals(optOptions.get("all"))) {
94              optimizeForInt=false;
95              // set other optimizations options to false here
96          } else {
97              if (Boolean.TRUE.equals(optOptions.get("indy"))) invokedynamic=true;
98              if (Boolean.FALSE.equals(optOptions.get("int"))) optimizeForInt=false;
99              if (invokedynamic) optimizeForInt=false;
100             // set other optimizations options to false here
101         }
102         this.classNode = cn;
103         this.outermostClass = null;
104         this.internalClassName = BytecodeHelper.getClassInternalName(classNode);
105 
106         bytecodeVersion = chooseBytecodeVersion(invokedynamic, config.getTargetBytecode());
107 
108         if (invokedynamic) {
109             try {
110                 this.invocationWriter = (InvocationWriter) indyWriter.newInstance(this);
111                 this.callSiteWriter = (CallSiteWriter) indyCallSiteWriter.newInstance(this);
112                 this.binaryExpHelper = (BinaryExpressionHelper) indyBinHelper.newInstance(this);
113             } catch (Exception e) {
114                 throw new GroovyRuntimeException("Cannot use invokedynamic, indy module was excluded from this build.");
115             }
116         } else {
117             this.callSiteWriter = new CallSiteWriter(this);
118             this.invocationWriter = new InvocationWriter(this);
119             this.binaryExpHelper = new BinaryExpressionHelper(this);
120         }
121         
122         this.unaryExpressionHelper = new UnaryExpressionHelper(this);
123         if (optimizeForInt) {
124             this.fastPathBinaryExpHelper = new BinaryExpressionMultiTypeDispatcher(this);
125             // todo: replace with a real fast path unary expression helper when available
126             this.fastPathUnaryExpressionHelper = new UnaryExpressionHelper(this);
127         } else {
128             this.fastPathBinaryExpHelper = this.binaryExpHelper;
129             this.fastPathUnaryExpressionHelper = new UnaryExpressionHelper(this);
130         }
131 
132         this.operandStack = new OperandStack(this);
133         this.assertionWriter = new AssertionWriter(this);
134         this.closureWriter = new ClosureWriter(this);
135         this.internalBaseClassName = BytecodeHelper.getClassInternalName(classNode.getSuperClass());
136         this.acg = asmClassGenerator;
137         this.sourceUnit = acg.getSourceUnit();
138         this.context = gcon;
139         this.compileStack = new CompileStack(this);
140         this.cv = cv;
141         if (optimizeForInt) {
142             this.statementWriter = new OptimizingStatementWriter(this);
143         } else {
144             this.statementWriter = new StatementWriter(this);
145         }
146         this.typeChooser = new StatementMetaTypeChooser();
147     }
148 
149     private static int chooseBytecodeVersion(final boolean invokedynamic, final String targetBytecode) {
150         if (invokedynamic) {
151             if (CompilerConfiguration.JDK8.equals(targetBytecode)) {
152                 return Opcodes.V1_8;
153             }
154             return Opcodes.V1_7;
155         } else {
156             if (CompilerConfiguration.JDK4.equals(targetBytecode)) {
157                 return Opcodes.V1_4;
158             }
159             if (CompilerConfiguration.JDK5.equals(targetBytecode)) {
160                 return Opcodes.V1_5;
161             }
162             if (CompilerConfiguration.JDK6.equals(targetBytecode)) {
163                 return Opcodes.V1_6;
164             }
165             if (CompilerConfiguration.JDK7.equals(targetBytecode)) {
166                 return Opcodes.V1_7;
167             }
168             if (CompilerConfiguration.JDK8.equals(targetBytecode)) {
169                 return Opcodes.V1_8;
170             }
171         }
172         throw new GroovyBugError("Bytecode version ["+targetBytecode+"] is not supported by the compiler");
173     }
174 
175     public AsmClassGenerator getAcg() {
176         return acg;
177     }
178 
179     public void setMethodVisitor(MethodVisitor methodVisitor) {
180         this.methodVisitor = methodVisitor;
181     }
182 
183     public MethodVisitor getMethodVisitor() {
184         return methodVisitor;
185     }
186 
187     public CompileStack getCompileStack() {
188         return compileStack;
189     }
190 
191     public OperandStack getOperandStack() {
192         return operandStack;
193     }
194 
195     public ClassNode getClassNode() {
196         return classNode;
197     }
198 
199     public CallSiteWriter getCallSiteWriter() {
200         return callSiteWriter;
201     }
202 
203     public ClassVisitor getClassVisitor() {
204         return cv;
205     }
206 
207     public ClosureWriter getClosureWriter() {
208         return closureWriter;
209     }
210     
211     public ClassVisitor getCv() {
212         return cv;
213     }
214 
215     public String getInternalClassName() {
216         return internalClassName;
217     }
218 
219     public InvocationWriter getInvocationWriter() {
220         return invocationWriter;
221     }
222 
223     public BinaryExpressionHelper getBinaryExpressionHelper() {
224         if (fastPath) {
225             return fastPathBinaryExpHelper;
226         } else {
227             return binaryExpHelper;
228         }
229     }
230 
231     public UnaryExpressionHelper getUnaryExpressionHelper() {
232         if (fastPath) {
233             return fastPathUnaryExpressionHelper;
234         } else {
235             return unaryExpressionHelper;
236         }
237     }
238 
239     public AssertionWriter getAssertionWriter() {
240         return assertionWriter;
241     }
242 
243     public TypeChooser getTypeChooser() {
244         return typeChooser;
245     }
246 
247     public String getInternalBaseClassName() {
248         return internalBaseClassName;
249     }
250     
251     public MethodNode getMethodNode() {
252         return methodNode;
253     }
254     
255     public void setMethodNode(MethodNode mn) {
256         methodNode = mn;
257         constructorNode = null;
258     }
259     
260     public ConstructorNode getConstructorNode(){
261         return constructorNode;
262     }
263     
264     public void setConstructorNode(ConstructorNode cn) {
265         constructorNode = cn;
266         methodNode = null;
267     }
268     
269     public boolean isNotClinit() {
270         return methodNode == null || !methodNode.getName().equals("<clinit>");
271     }
272 
273     public SourceUnit getSourceUnit() {
274         return sourceUnit;
275     }
276 
277     public boolean isStaticContext() {
278         if (compileStack!=null && compileStack.getScope()!=null) {
279             return compileStack.getScope().isInStaticContext();
280         }
281         if (!isInClosure()) return false;
282         if (constructorNode != null) return false;
283         return classNode.isStaticClass() || methodNode.isStatic();
284     }
285 
286     public boolean isInClosure() {
287         return classNode.getOuterClass() != null
288                 && classNode.getSuperClass() == ClassHelper.CLOSURE_TYPE;
289     }    
290     
291     public boolean isInClosureConstructor() {
292         return constructorNode != null
293         && classNode.getOuterClass() != null
294         && classNode.getSuperClass() == ClassHelper.CLOSURE_TYPE;
295     }
296 
297     public boolean isNotExplicitThisInClosure(boolean implicitThis) {
298         return implicitThis || !isInClosure();
299     }
300 
301 
302     public boolean isStaticMethod() {
303         return methodNode != null && methodNode.isStatic();
304     }
305 
306     public ClassNode getReturnType() {
307         if (methodNode != null) {
308             return methodNode.getReturnType();
309         } else if (constructorNode != null) {
310             return constructorNode.getReturnType();
311         } else {
312             throw new GroovyBugError("I spotted a return that is neither in a method nor in a constructor... I can not handle that");
313         }
314     }
315 
316     public boolean isStaticConstructor() {
317         return methodNode != null && methodNode.getName().equals("<clinit>");
318     }
319 
320     public boolean isConstructor() {
321         return constructorNode!=null;
322     }
323 
324     /**
325      * @return true if we are in a script body, where all variables declared are no longer
326      *         local variables but are properties
327      */
328     public boolean isInScriptBody() {
329         if (classNode.isScriptBody()) {
330             return true;
331         } else {
332             return classNode.isScript() && methodNode != null && methodNode.getName().equals("run");
333         }
334     }
335     
336     public String getClassName() {
337         String className;
338         if (!classNode.isInterface() || interfaceClassLoadingClass == null) {
339             className = internalClassName;
340         } else {
341             className = BytecodeHelper.getClassInternalName(interfaceClassLoadingClass);
342         }
343         return className;
344     }
345     
346     public ClassNode getOutermostClass() {
347         if (outermostClass == null) {
348             outermostClass = classNode;
349             while (outermostClass instanceof InnerClassNode) {
350                 outermostClass = outermostClass.getOuterClass();
351             }
352         }
353         return outermostClass;
354     }
355 
356     public GeneratorContext getContext() {
357         return context;
358     }
359 
360     public void setInterfaceClassLoadingClass(InterfaceHelperClassNode ihc) {
361         interfaceClassLoadingClass = ihc;
362     }
363     
364     public InterfaceHelperClassNode getInterfaceClassLoadingClass() {
365         return interfaceClassLoadingClass;
366     }
367     
368     public boolean shouldOptimizeForInt() {
369         return optimizeForInt;
370     }
371     
372     public StatementWriter getStatementWriter() {
373         return statementWriter;
374     }
375 
376     public void switchToFastPath() {
377         fastPath = true;
378         resetLineNumber();
379     }
380 
381     public void switchToSlowPath() {
382         fastPath = false;
383         resetLineNumber();
384     }
385 
386     public boolean isFastPath() {
387         return fastPath;
388     }
389     
390     public int getBytecodeVersion() {
391         return bytecodeVersion;
392     }
393     
394     public int getLineNumber() {
395         return lineNumber;
396     }
397     
398     public void setLineNumber(int n) {
399     	lineNumber = n;
400     }
401 
402 	public void resetLineNumber() {
403 		setLineNumber(-1);
404 	}
405 
406     public int getNextHelperMethodIndex() {
407         return helperMethodIndex++;
408     }
409 }